
`include "common_header.verilog"

//  *************************************************************************
//  File : stats_regreadarb
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited. 
//  Copyright (c) 2008 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Daniel Koehler
//  info@morethanip.com
//  *************************************************************************
//  Description : Host read arbitration and clock domain crossing.
//                Takes care on counter capture and providing the
//                upper half of the last read counter when odd address is read.
//                Read mux counters with configuration registers read data.
//  Version     : $Id: stats_regreadarb.v,v 1.5 2017/04/11 09:04:08 gc Exp $
//  *************************************************************************

module stats_regreadarb (

   reset_rxclk,
   reset_txclk,
   reset_reg_clk,
   rxclk,
   txclk,
   reg_clk,
   reg_rd,
   reg_sel,
   stat_data_out,
   stat_rd,
   stat_busy,
   stc_data_out,
   stc_sel,
   rx_stats_rd,
   rx_mreg_sel,
   rx_stats_busy,
   rx_stats_rdata,
   tx_stats_rd,
   tx_mreg_sel,
   tx_stats_busy,
   tx_stats_rdata);

parameter CNTWIDTH = 64;

input   reset_rxclk;                    //  Asynchronous Reset - rx_clk Domain
input   reset_txclk;                    //  Asynchronous Reset - tx_clk Domain
input   reset_reg_clk;                  //  Asynchronous Reset - reg_clk Domain
input   rxclk;                          //  xgmii rx Clock
input   txclk;                          //  xgmii tx Clock
input   reg_clk;                        //  ReferenceInterface Clock
input   reg_rd;                         //  Register Read Strobe
input   [7:0] reg_sel;                  //  Register Address
output  [31:0] stat_data_out;           //  Read Data to Host Bus
input   stat_rd;                        //  Statistics register address space read access
output  stat_busy;                      //  Interface Busy
input   [31:0] stc_data_out;            //  Read Data to Host Bus from control registers
input   stc_sel;                        //  A configuration register address is accessed (stc_data_out is valid)
output  rx_stats_rd;                    //  read request
output  [7:0] rx_mreg_sel;              //  true address
input   rx_stats_busy;                  //  statistics read busy
input   [CNTWIDTH-1:0] rx_stats_rdata;  //  statistics read data return
output  tx_stats_rd;                    //  read request
output  [7:0] tx_mreg_sel;              //  true address
input   tx_stats_busy;                  //  statistics read busy
input   [CNTWIDTH-1:0] tx_stats_rdata;  //  statistics read data return

reg     [31:0] stat_data_out; 
wire    stat_busy; 
wire    rx_stats_rd; 
wire    [7:0] rx_mreg_sel; 
wire    tx_stats_rd; 
wire    [7:0] tx_mreg_sel; 

reg     busy_int;               //  busy to host
wire    rx_stats_done;          //  handshaking
wire    tx_stats_done;          //  handshaking   
wire    reg_rd_rxstat;          //  reading rx stats
wire    reg_rd_txstat;          //  reading tx stats
reg     [CNTWIDTH - 1:0] stats_rdata_l; //  statistics read data return
wire    [31:0] stats_rdata_hi;  //  upper 32 bit (odd address)
reg     [1:0] data_sel;         //  read mux

//  rxclk
wire    rx_stats_busy_n; //  inverted

//  txclk
wire    tx_stats_busy_n; //  inverted

//  ------------------------------------------------------
//  Determine statistics register access and indicate busy 
//  if there is a statistics register currently accessed.
//  ------------------------------------------------------

//  Start read capture, only when lower address (even address) is accessed. 
//  Odd addresses MUST NOT trigger a new read to allow consistent readback.

//  determine if RX statistics are accessed: 0x0100 .. 0x01ff (0x40..0x7f) 32 counters
//                                           0x0380 .. 0x03bf (0xe0..0xef) 8 PFC class counters
assign reg_rd_rxstat = (reg_rd == 1'b 1 & reg_sel[0] == 1'b 0 & 
                        ((reg_sel > 8'h 3f & reg_sel < 8'h 80)
                        `ifdef STAT_PFCCNT_ENA
                         `ifdef MTIP_M128_PFC_16PRI
                         | (reg_sel > 8'h bf & reg_sel < 8'h c0) // Relocate counters from 192..223
                         `else
                         | (reg_sel > 8'h df & reg_sel < 8'h f0) // Standard location in 224..239
                         `endif
                        `endif
                        )
                       ) ? 1'b 1 : 1'b 0; 

//  determine if TX statistics are accessed: 0x0200 .. 0x02ff (0x80..0xbf) 32 counters                      
//                                           0x03c0 .. 0x03ff (0xf0..0xff) 8 PFC class counters
assign reg_rd_txstat = (reg_rd == 1'b 1 & reg_sel[0] == 1'b 0 & 
                        ((reg_sel > 8'h 7f & reg_sel < 8'h c0)
                        `ifdef STAT_PFCCNT_ENA
                         `ifdef MTIP_M128_PFC_16PRI
                         | (reg_sel > 8'h bf)   // Relocate counters from 224..255
                         `else
                         | (reg_sel > 8'h ef)   // Standard location in 240...255
                         `endif
                        `endif
                        )
                       ) ? 1'b 1 : 1'b 0; 

//  busy handshake return
//  ---------------------
always @(posedge reset_reg_clk or posedge reg_clk)
   begin : process_1
   if (reset_reg_clk == 1'b 1)
      begin
      busy_int <= 1'b 0;	
      end
   else
      begin

//  detect if statistics registers are accessed to return the busy handshaking signal
//  Also ensure that any access to the statistics register address space (stat_rd) is 
//  acknowledged to avoid hang if unused address is accessed.

      if (rx_stats_done == 1'b 1 | tx_stats_done == 1'b 1 | 
          stat_rd == 1'b 1 & reg_rd_rxstat == 1'b 0 & reg_rd_txstat == 1'b 0)
         begin
         busy_int <= 1'b 0;	//  handshake to host_control, cycle done, read data is valid
         end
      else
         begin
         busy_int <= 1'b 1;	//  handshake to host_control, allowing a new cycle to start
         end

      end
   end

assign stat_busy = busy_int; //  busy handshake to host_control


//  ------------------------------------------------------
//  transfer read request into line clock domains
//  ------------------------------------------------------
//  note: we use the _tog variant, as it cannot get into a race condition 
//        as the reg_rd_rxstat will stay asserted until the busy handshake comes back,
//        which then may not be enough time to clear the handshaking within the "redge_ckxing" (without _tog) inbetween consecutive reads.

redge_ckxing_tog U_RXRDREQ (
          .reset(reset_reg_clk),
          .clk(reg_clk),
          .sig(reg_rd_rxstat),
          .reset_clk_o(reset_rxclk),
          .clk_o(rxclk),
          .sig_o(rx_stats_rd));

redge_ckxing_tog U_TXRDREQ (
          .reset(reset_reg_clk),
          .clk(reg_clk),
          .sig(reg_rd_txstat),
          .reset_clk_o(reset_txclk),
          .clk_o(txclk),
          .sig_o(tx_stats_rd));

mtip_xsync #(8) U_RXADR (
          .data_in(reg_sel),
          .reset(reset_rxclk),
          .clk(rxclk),
          .data_s(rx_mreg_sel));

mtip_xsync #(8) U_TXADR (
          .data_in(reg_sel),
          .reset(reset_txclk),
          .clk(txclk),
          .data_s(tx_mreg_sel));

//  ------------------------------------------------------
//  return busy falling edge to reg_clk (handshake return)
//  ------------------------------------------------------

assign rx_stats_busy_n = ~rx_stats_busy; //  get a rising edge

redge_ckxing_tog U_RXRET (
          .reset(reset_rxclk),
          .clk(rxclk),
          .sig(rx_stats_busy_n),
          .reset_clk_o(reset_reg_clk),
          .clk_o(reg_clk),
          .sig_o(rx_stats_done));


assign tx_stats_busy_n = ~tx_stats_busy; //  pulse

//  get a rising edge
redge_ckxing_tog U_TXRET (
          .reset(reset_txclk),
          .clk(txclk),
          .sig(tx_stats_busy_n),
          .reset_clk_o(reset_reg_clk),
          .clk_o(reg_clk),
          .sig_o(tx_stats_done));

//  ------------------------------------------------------
//  Capture read data in reg_clk domain.
//  Note: the inputs from the individual stats module
//        is constant (stored in output register). 
//        The "done" pulse created (rx/tx_stats_done) is after
//        the data was latched in the line clock domain.
//        Hence the input to the reg_clk domain then is 
//        expected to be stable for at least 2 reg_clk cycles 
//        (latency within the stats_done syncer module),
//        avoiding metastability.
//        Therefore the double-DFF (which would add 256 registers)
//        is omitted here and we directly feed the selection mux.
//  ------------------------------------------------------

always @(posedge reset_reg_clk or posedge reg_clk)
   begin : process_2
   if (reset_reg_clk == 1'b 1)
      begin
      stats_rdata_l <= {(CNTWIDTH){1'b 0}};
      data_sel      <= 2'b 00;	
      end
   else
      begin
      if (rx_stats_done == 1'b 1)
         begin
         stats_rdata_l <= rx_stats_rdata;	//  multicycle !
         end
      else if (tx_stats_done == 1'b 1 )
         begin
         stats_rdata_l <= tx_stats_rdata;	//  multicycle !
         end

        //  control the output mux
      if (stat_rd == 1'b 1 & stc_sel == 1'b 1)
         begin
         data_sel <= 2'b 11;	//  configuration registers of this module
         end
      else if (stat_rd == 1'b 1 & reg_sel[0] == 1'b 0 )
         begin
         data_sel <= 2'b 01;	//  LSBs
         end
      else if (stat_rd == 1'b 1 & reg_sel[0] == 1'b 1 )
         begin
         data_sel <= 2'b 10;	//  MSBs
         end
      else
         begin
         data_sel <= 2'b 00;	//  zeroes
         end

      end
   end

//  return upper/lower half of the data

//  fill unused bits with 0 on upper half
generate if(CNTWIDTH<64)
begin:zfill

assign stats_rdata_hi[31:CNTWIDTH - 32]    = {(32 - (CNTWIDTH - 32)){1'b 0}}; 

end
endgenerate

assign stats_rdata_hi[CNTWIDTH - 32 - 1:0] = stats_rdata_l[CNTWIDTH - 1:32]; 

always @(data_sel or stats_rdata_l or stats_rdata_hi or stc_data_out)
   begin : process_3
   case (data_sel)
   2'b 01:
      begin
      stat_data_out = stats_rdata_l[31:0];	//  lower half on even addresses
      end
   2'b 10:
      begin
      stat_data_out = stats_rdata_hi;	//  upper half on odd addresses  
      end
   2'b 11:
      begin
      stat_data_out = stc_data_out;	//  configuration registers read
      end
   2'b 00:
      begin
      stat_data_out = 32'h 00000000;	
      end
   default:
      begin
      stat_data_out = 32'h 00000000;	
      end

   endcase
   end

endmodule // module stats_regreadarb
